package ui;

import io.DataAccess;
import io.UserAccess;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;

import net.miginfocom.swing.MigLayout;

import org.jdom2.JDOMException;

import users.User;

/**
 * The login window is the first window that users will encounter,
 * and it serves as a portal to the application.  Using this window,
 * the user can either log in as normal, or they can fill out an
 * application to register into the system and subsequently log in
 * as normal.
 * 
 * @author Aaron Chen
 * @version 3/6/2014
 */
@SuppressWarnings("serial")
public class LoginPage extends JFrame {
  
  /** The String for First Name. */
  private static final String FIRST_NAME = "First Name";
  
  /** The String for Last Name.*/
  private static final String LAST_NAME = "Last Name";
  
  /** The String for Login.*/
  private static final String LOGIN = "Login";
  
  /** The String for Register.*/
  private static final String REGISTER = "Register";
  
  /** The card identifier for the login panel.*/
  private static final String CARD_LOGIN_PANEL = "Login Panel";
  
  /** The card identifier for the registration panel.*/
  private static final String CARD_REGISTRATION_PANEL = "Registration Panel";

  /** The parent component that this window belongs to. */
  private MainPage my_parent;
  
  /** The panel that holds the card layout. */
  private JPanel my_content_pane;
  
  /** The layout that is responsible for the various views. */
  private CardLayout my_card_layout = new CardLayout(0, 0);
  
  /** The text field for entering the user's first name. */
  private JTextField my_tf_first_name;
  
  /** The text field for entering the user's last name. */
  private JTextField my_tf_last_name;
  
  /** The object through which database access is gained. */
  private DataAccess my_data;
  
  /** The String representation of the information a registering
   * user needs to supply. */
  private final String[] my_registration_fields = {
    FIRST_NAME,
    LAST_NAME,
    "Email Address"
  };
  
  /** The text fields for entering the information a registering
   * user needs to supply. */
  private JTextField[] my_tf_registration_fields; 

  
  /**
   * Create the log in page.
   * @param the_parent The parent component for the login window.
   */
  public LoginPage(final MainPage the_parent) {
    setTitle(LOGIN);
    my_data = getDataAccess();
    my_content_pane = new JPanel();
    my_content_pane.setBorder(Standards.BORDER_EMPTY);
    setContentPane(my_content_pane);
    my_content_pane.setLayout(my_card_layout);
    my_parent = the_parent;
    
    createCardPanels();
    pack();
  }
  
  /**
   * Attempts to create a DataAccess connection to the database.
   * @return The DataAccess object.
   */
  private DataAccess getDataAccess() {
    DataAccess database = null;
    try {
      database = new DataAccess();
    } catch (final IOException e) {
      JOptionPane.showMessageDialog(this, "There is a problem accessing the database.");
      e.printStackTrace();
    } catch (final JDOMException e) {
      JOptionPane.showMessageDialog(this, "The file specified by the code is incorrect.");
      e.printStackTrace();
    }
    return database;
  }
  
  /**
   * Creates the individual panels for the card layout.
   */
  private void createCardPanels() {
    final JPanel login_panel = makeLoginPanel();
    my_content_pane.add(login_panel);
    my_card_layout.addLayoutComponent(login_panel, CARD_LOGIN_PANEL);
    final JPanel registration_panel = makeRegistrationPanel();
    my_content_pane.add(registration_panel);
    my_card_layout.addLayoutComponent(registration_panel, CARD_REGISTRATION_PANEL);
  }

  /** 
   * Creates the login panel; the default panel that users will see when
   * they open the login window.
   * @return The login panel.
   */
  private JPanel makeLoginPanel() {
    final JPanel login_panel = new JPanel();
    login_panel.setLayout(new BoxLayout(login_panel, BoxLayout.Y_AXIS));
    
    final JPanel north_panel = new JPanel();
    login_panel.add(north_panel);
    north_panel.setLayout(new BorderLayout(0, 0));
    
    final JPanel text_fields_panel = new JPanel();
    north_panel.add(text_fields_panel, BorderLayout.SOUTH);
    text_fields_panel.setLayout(new BoxLayout(text_fields_panel, BoxLayout.Y_AXIS));
    
    final JPanel first_name_panel = new JPanel();
    text_fields_panel.add(first_name_panel);
    
    text_fields_panel.add(Box.createVerticalStrut(Standards.STRUT_LENGTH_MEDIUM));
    
    final JPanel last_name_panel = new JPanel();
    text_fields_panel.add(last_name_panel);
    
    final JLabel lbl_first_name = new JLabel(FIRST_NAME);
    lbl_first_name.setHorizontalAlignment(SwingConstants.RIGHT);
    final JLabel lbl_last_name = new JLabel(LAST_NAME);
    lbl_last_name.setHorizontalAlignment(SwingConstants.RIGHT);
    final Dimension d =
        new Dimension((int) Math.max(lbl_last_name.getPreferredSize().getWidth(),
                                     lbl_first_name.getPreferredSize().getWidth()), 
                      (int) lbl_last_name.getPreferredSize().getHeight());
    lbl_first_name.setPreferredSize(d);
    lbl_last_name.setPreferredSize(d);
    last_name_panel.add(lbl_last_name);
    first_name_panel.add(lbl_first_name);

    my_tf_first_name = new JTextField();
    my_tf_first_name.setColumns(Standards.TEXT_BOX_HALF_WIDTH);
    first_name_panel.add(my_tf_first_name);
    
    my_tf_last_name = new JTextField();
    my_tf_last_name.setColumns(Standards.TEXT_BOX_HALF_WIDTH);
    last_name_panel.add(my_tf_last_name);
    
    final JPanel south_panel = new JPanel();
    login_panel.add(south_panel);
    south_panel.setLayout(new BorderLayout(0, 0));
    
    final JPanel button_panel = new JPanel();
    south_panel.add(button_panel, BorderLayout.NORTH);
    
    final JButton btn_login = new JButton(LOGIN);
    btn_login.setMnemonic('L');
    
    final JButton btn_register = new JButton(REGISTER);
    btn_register.addActionListener(new ActionListener() {
      /** The action to perform when the Register button is clicked.
       * Switches the panel to the registration panel. */
      @Override
      public void actionPerformed(final ActionEvent the_e) {
        my_card_layout.show(my_content_pane, CARD_REGISTRATION_PANEL);
      }
    });
    btn_register.setMnemonic('R');

    final ActionListener al = new AuthenticationAction();
    btn_login.addActionListener(al);
    my_tf_first_name.addActionListener(al);
    my_tf_last_name.addActionListener(al);
    
    button_panel.add(btn_login);
    button_panel.add(btn_register);
    
    return login_panel;
  }
  
  /**
   * Creates the registration panel: the panel where new users can
   * register into the system.
   * @return The registration panel.
   */
  private JPanel makeRegistrationPanel() {
    final JPanel registration_panel = new JPanel();
    registration_panel.setLayout(new BoxLayout(registration_panel, BoxLayout.Y_AXIS));
    
    final JPanel north_panel = new JPanel(new BorderLayout());
    
    final JPanel text_fields_panel = new JPanel(new MigLayout("", "[grow]", "[][][][][]"));
    final JLabel title_label = new JLabel("Registration");
    title_label.setFont(Standards.FONT_TITLE);
    text_fields_panel.add(title_label, "cell 0 0,alignx center,aligny top");
    
    my_tf_registration_fields = new JTextField[my_registration_fields.length];
    for (int i = 0; i < my_registration_fields.length; i++) {
      my_tf_registration_fields[i] = new JTextField();
      text_fields_panel.add(makeLabelTextFieldGroup(my_registration_fields[i],
                                                    my_tf_registration_fields[i]),
                                                    "cell 0 " + (i + 1)
                                                    + ",alignx center,aligny top");
    }
    
    final JPanel south_panel = new JPanel(new BorderLayout());
    
    final JPanel buttons_panel = new JPanel();
    final JButton btn_register = new JButton(REGISTER);
    btn_register.setMnemonic('R');
    btn_register.addActionListener(new RegistrationAction());
    final JButton btn_cancel = new JButton("Cancel");
    btn_cancel.setMnemonic('C');
    btn_cancel.addActionListener(new ActionListener() {
      /** The action to be performed.
       * Shows the login panel when the Cancel button is clicked. */
      @Override
      public void actionPerformed(final ActionEvent the_e) {
        my_card_layout.show(my_content_pane, CARD_LOGIN_PANEL);
      }
    });
    buttons_panel.add(btn_register);
    buttons_panel.add(btn_cancel);
    
    north_panel.add(text_fields_panel, BorderLayout.SOUTH);
    south_panel.add(buttons_panel, BorderLayout.NORTH);
    
    registration_panel.add(north_panel);
    registration_panel.add(south_panel);
    
    return registration_panel;
  }
  
  /**
   * Makes a panel that contains a label and its text field in a small,
   * compact unit.
   * @param the_str The string for the label.
   * @param the_text_field The textfield for input.
   * @return  The panel with a label and textfield in a specific arrangement.
   */
  private JPanel makeLabelTextFieldGroup(final String the_str,
                                         final JTextField the_text_field) {
    final JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    
    the_text_field.setColumns(Standards.TEXT_BOX_HALF_WIDTH);
    
    final JLabel label = new JLabel(the_str);
    
    panel.add(label);
    panel.add(the_text_field);
    
    return panel;
  }
  
  /**
   * The action that authenticates user information.
   * 
   * @author Aaron Chen
   * @version 3/6/2014
   */
  private class AuthenticationAction implements ActionListener {
    
    /**
     * The action to be performed when the button is pressed.
     * The user's input is verified for any obvious failure.  If it passes,
     * then the method queries the database to see if the user data exists.
     * If it does, then access to the system is granted to the user.
     * Otherwise, the user is denied access.  The user is informed of the
     * result.  The nature of the failure, if any, is not specified. 
     * @param the_e The event.
     */
    @Override
    public void actionPerformed(final ActionEvent the_e) {
      final UserAccess database = my_data.accessUser();
      final String fstr = my_tf_first_name.getText().trim();
      final String lstr = my_tf_last_name.getText().trim();
      final User u = database.getUser(fstr, lstr);
      if (u == null) {
        JOptionPane.showMessageDialog(LoginPage.this,
                                      "Access Denied!\n" + "This person is not registered!");
      } else {
        final StringBuilder sb = new StringBuilder();
        sb.append("Welcome ");
        sb.append(u.getFirstName());
        sb.append(" ");
        sb.append(u.getLastName());
        sb.append(" Your ID is ");
        sb.append(u.getID());
        JOptionPane.showMessageDialog(LoginPage.this, sb.toString());
        my_parent.setRoles(u);
        my_tf_first_name.setText(null);
        my_tf_last_name.setText(null);
        LoginPage.this.dispose();
      }
    }
    
  }
  
  /**
   * The action that registers the user into the system.
   * 
   * @author Aaron Chen
   * @version 3/6/2014
   */
  private class RegistrationAction implements ActionListener {
    
    /**
     * The action to be performed when the button is pressed.
     * The user's input is verified for any obvious failures.  If it passes,
     * then the method queries the database and attempts to write the user's
     * information into the database, registering the user.  If it fails,
     * then nothing happens.  In either case, the user is notified of the
     * outcome.
     * @param the_e The event.
     */
    @Override
    public void actionPerformed(final ActionEvent the_e) {
      boolean null_field_detected = false;
      for (JTextField tf : my_tf_registration_fields) {
        if (tf.getText().isEmpty()) {
          null_field_detected = true;
        }
      }
      if (null_field_detected) {
        JOptionPane.showMessageDialog(LoginPage.this,
                                      "One or more fields have not been filled in.");
        return;
      }
      
      final User user = new User(my_tf_registration_fields[0].getText(),
                                 my_tf_registration_fields[1].getText(),
                                 my_tf_registration_fields[2].getText());
      my_data.accessUser().createUser(user);
      
      JOptionPane.showMessageDialog(LoginPage.this, "Registration successful!");
      my_card_layout.show(my_content_pane, CARD_LOGIN_PANEL);
    }
  }
}
